#include "Engine.h"
#include "..\utils\Timer.h"
#include "..\base\Service.h"
#include "..\utils\Log.h"
#include "Controller.h"
#include "..\content\ResManager.h"
#include "..\renderer\DebugRenderer.h"
#include "..\game\World.h"
#include "..\utils\Profiler.h"
#include "..\cvar\CVar.h"
#include "..\collision\CollisionDetector.h"

ds::Engine *gEngine;

namespace ds {
// --------------------------------------
// create the global Engine
// --------------------------------------
Engine::Engine() : m_Initialized(false) , m_ButtonDown(-1) , m_ButtonUp(-1) {	
	gProfiler = new NewProfiler();
	gCV = new CVarContainer();
	rand.seed(GetTickCount());
}

// --------------------------------------
// Destructor - Deleting all objects
// --------------------------------------
Engine::~Engine(void) {	
	
	delete m_Audio;
	delete m_GameStateManager;
	delete m_Renderer;
	delete m_Input;
	delete m_ActionManager;
	delete m_World;
	delete m_CollisionDetector;
	delete m_EventManager;
	delete m_DrawCounter;
	delete m_DebugRenderer;	
	delete m_DeclarationManager;
	delete m_ResourceManager;
}
// --------------------------------------
// Initialize the engine
// --------------------------------------
void Engine::init(HWND hWnd,const Settings& settings,Rect wndPosition) {
	m_hWnd = hWnd;
	mode = settings.mode;
	width = settings.width;
	height = settings.height;
	
	m_EventManager = new EventManager;
	m_CollisionDetector = new CollisionDetector();
	m_Input = new Input(hWnd);

	m_ResourceManager = new ResManager();
	m_World = new World();
	//m_ComponentManager = new ComponentManager();
	m_Renderer = new Renderer(m_hWnd,settings);   
	m_Renderer->init();
	
	m_GameStateManager = new GameStateManager();
	m_Audio = new AudioManager();
	m_Audio->initialize(hWnd);
	updateAllowed = true;
	//fpsTimer = new CTimer();
	windowPosition = wndPosition;	
	m_DrawCounter = new DrawCounter();
	m_DrawCounter->reset();
	m_DebugRenderer = new DebugRenderer();
	m_Viewport = new Viewport(settings.width,settings.height);
	m_ActionManager = new ActionManager();
	m_DeclarationManager = new DeclarationManager;
}

void Engine::createViewport(int width,int height) {
	delete m_Viewport;
	m_Viewport = new Viewport(width,height);
}

// ------------------------------------------------------------------
// This method will initialize all services. Then it will initialize
// all game components and then call loadContent for every
// game component
// ------------------------------------------------------------------
void Engine::initializeComponents() {
	LOG(logINFO) << "initialize services";
	// initialize all services
	for ( size_t i = 0; i < m_Services.size(); ++i ) {
		LOG(logINFO) << "service: " << m_Services[i]->getName().c_str();
		m_Services[i]->initialize();
	}
	//m_ComponentManager->initialize(*resourceManager);	
}

void Engine::initializeNodes() {
	m_World->initializeNodes(*m_ResourceManager);
}

void Engine::buildFrame(float elapsedTime) {	
	gProfiler->reset();
	PR_START("MAIN")
	m_ElapsedTime = elapsedTime;
	if ( mode == 1 ) {
		m_ElapsedTime = 0.01666f;
	}
	m_DebugRenderer->clear();
	PR_START("UPDATE_FRAME")
	updateFrame(m_ElapsedTime);	
	PR_END("UPDATE_FRAME")
	PR_START("RENDER")
	m_DrawCounter->reset();
	getRenderer()->render();	
	PR_END("RENDER")

	PR_START("DEBUG_RENDER")
	m_DebugRenderer->draw();
	PR_END("DEBUG_RENDER")
	PR_END("MAIN")
	getRenderer()->endRendering();		
}

// --------------------------------------
// calls update for all necessary objects
// --------------------------------------
void Engine::updateFrame(float elapsedTime) {
	if ( isUpdateAllowed() ) {	
#ifdef DEBUG
		PR_START("CHECK_RELOAD")
		m_DeclarationManager->checkFiles(elapsedTime);
		PR_END("CHECK_RELOAD")
#endif
		m_EventManager->reset();
		m_CollisionDetector->reset();
		// handle key and mouse events
		processButtons();
		m_Input->update(elapsedTime);

		// update services
		PR_START("UPDATE_COMPONENTS")
		for ( size_t i = 0; i < m_Services.size(); ++i) {
			if ( m_Services[i]->isActive() ) {
				PR_START(m_Services[i]->getName().c_str())
				m_Services[i]->update(elapsedTime);
				PR_END(m_Services[i]->getName().c_str())
			}
		}
		PR_END("UPDATE_COMPONENTS")

		PR_START("UPDATE_WORLD")
		m_World->update(elapsedTime);
		PR_END("UPDATE_WORLD")

		
		// first call engine update to update all subsystems
			
		// now handle update of the actual game		
		PR_START("UPDATE_GAMESTATE")
		if ( m_GameStateManager->getActiveState() != NULL ) {
			m_GameStateManager->getActiveState()->update(elapsedTime);
		}
		PR_END("UPDATE_GAMESTATE")

		PR_START("UPDATE_PHYSICS")
		m_CollisionDetector->update();
		m_CollisionDetector->processBuffer();
		PR_END("UPDATE_PHYSICS")

		PR_START("ACTIONS")
		m_ActionManager->update(elapsedTime);
		PR_END("ACTIONS")

		PR_START("PROCESS_EVENTS")
		m_EventManager->processBuffer();
		PR_END("PROCESS_EVENTS")

		if ( !m_NextState.empty() ) {
			m_GameStateManager->activate(m_NextState);
			m_NextState = "";
		}
	}		
}

// --------------------------------------------
// all keyboard and mouse controller methods
// --------------------------------------------
void Engine::OnKeyDown(WPARAM virtualKeyCode) {
	ControllerList::iterator i = controllers.begin();
	ControllerList::iterator end = controllers.end();	
	while ( i != end ) {
		(*i)->OnKeyDown(virtualKeyCode);
		++i;
	}
}

void Engine::OnKeyUp(WPARAM virtualKeyCode) {
	ControllerList::iterator i = controllers.begin();
	ControllerList::iterator end = controllers.end();	
	while ( i != end ) {
		(*i)->OnKeyUp(virtualKeyCode);
		++i;
	}
}

void Engine::OnChar(char ascii,unsigned int keyState) {	
	ControllerList::iterator i = controllers.begin();
	ControllerList::iterator end = controllers.end();	
	while ( i != end ) {
		(*i)->OnChar(ascii,keyState);
		++i;
	}	
}

void Engine::addController(KeyboardController *controller) {	
	controllers.push_back(controller);
}

void Engine::processButtons() {
	if ( m_ButtonDown != -1 ) {
		for ( size_t i = 0; i < mouseController.size();++i ) {
			mouseController[i]->OnButtonDown(m_ButtonDown);
		}
		/*
		MouseControllerList::iterator i = mouseController.begin();
		MouseControllerList::iterator end = mouseController.end();	
		while ( i != end ) {
			(*i)->OnButtonDown(m_ButtonDown);
			++i;
		}
		*/
		m_ButtonDown = -1;
	}
	if ( m_ButtonUp != -1 ) {
		for ( size_t i = 0; i < mouseController.size();++i ) {
			mouseController[i]->OnButtonUp(m_ButtonUp);
		}
		/*
		MouseControllerList::iterator i = mouseController.begin();
		MouseControllerList::iterator end = mouseController.end();	
		while ( i != end ) {
			(*i)->OnButtonUp(m_ButtonUp);
			++i;
		}
		*/
		m_ButtonUp = -1;
	}
}

void Engine::shutdown() {
	PostMessage(m_hWnd,WM_CLOSE,NULL,NULL);
}

void Engine::addMouseController(MouseController *controller) {
	mouseController.push_back(controller);
}


LPDIRECT3DDEVICE9 Engine::getDevice() { 
	return m_Renderer->getDevice();
}

Renderer* Engine::getRenderer() {
	return m_Renderer;
}

Input* Engine::getInput() {
	return m_Input;
}

// -----------------------------------------
// Adds a service to the list of services
// [in] service - the service 
// -----------------------------------------
void Engine::addService(Service *service) {
	m_Services.push_back(service);
}

Service* Engine::getService(const std::string& name) {
	for ( size_t i = 0; i < m_Services.size(); ++i ) {
		if ( m_Services[i]->getName() == name ) {
			return m_Services[i];
		}
	}
	return 0;
}

void Engine::debug() {
	LOGC(logINFO,"Engine") << "---------- DEBUG OUTPUT ----------";
	LOGC(logINFO,"Engine") << "---------- Sounds       ----------";
	m_Audio->debug();
	LOGC(logINFO,"Engine") << "---------- Declarations ----------";
	m_DeclarationManager->debug();
	LOGC(logINFO,"Engine") << "---------- Game states  ----------";
	m_GameStateManager->debug();
	LOGC(logINFO,"Engine") << "---------- World        ----------";
	m_World->debug();
	LOGC(logINFO,"Engine") << "---------- Resources    ----------";
	m_ResourceManager->debug();
	LOGC(logINFO,"Engine") << "---------- Actions      ----------";
	m_ActionManager->debug();
	LOGC(logINFO,"Engine") << "---------- DEBUG OUTPUT ----------";
}

};
